package com.zhx.common.chat.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import com.zhx.common.chat.dao.ContactDao;
import com.zhx.common.chat.dao.MessageDao;
import com.zhx.common.chat.domain.dto.RoomBaseInfo;
import com.zhx.common.chat.domain.entity.*;
import com.zhx.common.chat.domain.enums.RoomTypeEnum;
import com.zhx.common.chat.domain.vo.response.ChatRoomResp;
import com.zhx.common.chat.service.RoomAppService;
import com.zhx.common.chat.service.RoomService;
import com.zhx.common.chat.service.adapter.ChatAdapter;
import com.zhx.common.chat.service.cache.HotRoomCache;
import com.zhx.common.chat.service.cache.RoomCache;
import com.zhx.common.chat.service.cache.RoomFriendCache;
import com.zhx.common.chat.service.cache.RoomGroupCache;
import com.zhx.common.chat.service.strategy.msg.AbstractMsgHandler;
import com.zhx.common.chat.service.strategy.msg.MsgHandlerFactory;
import com.zhx.common.common.domain.vo.req.CursorPageBaseReq;
import com.zhx.common.common.domain.vo.resp.CursorPageBaseResp;
import com.zhx.common.common.util.AssertUtil;
import com.zhx.common.user.domain.entity.User;
import com.zhx.common.user.service.cache.UserInfoCache;
import io.swagger.models.auth.In;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class RoomAppServiceImpl implements RoomAppService {

    @Resource
    private ContactDao contactDao;
    @Resource
    private HotRoomCache hotRoomCache;
    @Resource
    private UserInfoCache userInfoCache;
    @Resource
    private RoomCache roomCache;
    @Resource
    private RoomGroupCache roomGroupCache;
    @Resource
    private MessageDao messageDao;
    @Resource
    private RoomFriendCache roomFriendCache;
    @Resource
    private RoomService roomService;

    /**
     * 获取联系人页面信息。
     * 根据用户是否登录，查询不同的数据源，并将结果合并后返回。
     *
     * @param request 分页请求参数，包含查询条件和分页信息。
     * @param uid 用户ID，如果用户已登录，则非空；如果用户未登录，则为null。
     * @return CursorPageBaseResp<ChatRoomResp> 包含会话列表的响应对象，会话列表为ChatRoomResp类型。
     */
    @Override
    public CursorPageBaseResp<ChatRoomResp> getContactPage(CursorPageBaseReq request, Long uid) {
        // 根据用户登录状态选择不同的数据处理逻辑
        CursorPageBaseResp<Long> page;
        if (Objects.nonNull(uid)) {
            // 用户已登录，需要查询用户的基础会话和热门房间
            // 前端传过来的游标（时间）是最上一页最后的，这一页最新的（时间最新,最大）。
            Double hotEnd = getCursorOrNull(request.getCursor());
            Double hotStart = null;

            // 查询用户的基础会话，并获取会话ID列表 -> 游标查询
            CursorPageBaseResp<Contact> contactPage = contactDao.getContactPage(uid, request);
            List<Long> baseRoomIds = contactPage.getList().stream().map(Contact::getRoomId).collect(Collectors.toList());

            // 如果不是最后一页，则获取下一页的起始cursor
            if (!contactPage.getIsLast()) {
                hotStart = getCursorOrNull(contactPage.getCursor());
            }

            // 查询热门房间，并获取房间ID列表
            Set<ZSetOperations.TypedTuple<String>> typedTuples = hotRoomCache.getRoomRange(hotStart, hotEnd);
            List<Long> hotRoomIds = typedTuples.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull).map(Long::parseLong).collect(Collectors.toList());

            // 合并基础会话和热门房间的ID列表
            baseRoomIds.addAll(hotRoomIds);
            page = CursorPageBaseResp.init(contactPage, baseRoomIds);
        } else {
            // 用户未登录，只查询全局热门房间
            CursorPageBaseResp<Pair<Long, Double>> roomCursorPage = hotRoomCache.getRoomCursorPage(request);
            List<Long> roomIds = roomCursorPage.getList().stream().map(Pair::getKey).collect(Collectors.toList());
            page = CursorPageBaseResp.init(roomCursorPage, roomIds);
        }

        // 组装会话信息，包括名称、头像和未读数等 page.getList() ->RoomIds
        List<ChatRoomResp> result = buildContactResp(uid, page.getList());
        return CursorPageBaseResp.init(page, result);
    }

    @Override
    public ChatRoomResp getContactDetail(Long uid, long roomId) {
        Room room = roomCache.get(roomId);
        AssertUtil.isNotEmpty(room, "房间号有误");
        return buildContactResp(uid, Collections.singletonList(roomId)).get(0);
    }

    @Override
    public ChatRoomResp getContactDetailByFriend(Long uid, Long friendUid) {
        RoomFriend friendRoom = roomService.getFriendRoom(uid, friendUid);
        AssertUtil.isNotEmpty(friendRoom, "他不是您的好友");
        return buildContactResp(uid, Collections.singletonList(friendRoom.getRoomId())).get(0);
    }

    /**
     * 构建联系人响应列表。
     *
     * @param uid 用户ID，用于获取用户相关数据。
     * @param roomIds 聊天室ID列表，用于获取聊天室相关信息。
     * @return 返回一个包含聊天室响应信息的列表，每个响应包含聊天室的基本信息、最后一条消息、未读消息数等。
     */
    private List<ChatRoomResp> buildContactResp(Long uid, List<Long> roomIds) {
        // 获取聊天室基本表情和头像信息
        Map<Long, RoomBaseInfo> roomBaseInfoMap = getRoomBaseInfoMap(roomIds, uid);
        // 获取指定聊天室ID列表中每个聊天室的最后一条消息ID，并查询这些消息
        List<Long> msgIds = roomBaseInfoMap.values().stream().map(RoomBaseInfo::getLastMsgId).collect(Collectors.toList());
        List<Message> messages = CollectionUtil.isEmpty(msgIds) ? new ArrayList<>() : messageDao.listByIds(msgIds);
        // 将消息ID映射到消息对象
        Map<Long, Message> msgMap = messages.stream().collect(Collectors.toMap(Message::getId, Function.identity()));
        // 根据消息发送者ID获取用户信息，用于构造消息内容显示
        Map<Long, User> lastMsgUidMap = userInfoCache.getBatch(messages.stream().map(Message::getFromUid).collect(Collectors.toList()));
        // 获取指定用户ID和聊天室ID列表的未读消息数量
        Map<Long, Integer> unReadCountMap = getUnReadCountMap(uid, roomIds);

        return roomBaseInfoMap.values().stream().map(room -> {
            ChatRoomResp resp = new ChatRoomResp();
            RoomBaseInfo roomBaseInfo = roomBaseInfoMap.get(room.getRoomId());
            // 设置聊天室基本属性
            resp.setAvatar(roomBaseInfo.getAvatar());
            resp.setRoomId(room.getRoomId());
            resp.setActiveTime(room.getActiveTime());
            resp.setHot_Flag(roomBaseInfo.getHotFlag());
            resp.setType(roomBaseInfo.getType());
            resp.setName(roomBaseInfo.getName());
            // 如果存在最后一条消息，则设置消息内容和发送者名称
            Message message = msgMap.get(room.getLastMsgId());
            if (Objects.nonNull(message)) {
                AbstractMsgHandler strategyNoNull = MsgHandlerFactory.getStrategyNoNull(message.getType());
                Long fromUid = message.getFromUid();
                String name = lastMsgUidMap.get(fromUid).getName();
                resp.setText(name + ":" + strategyNoNull.showContactMsg(message));
            }
            // 设置未读消息数量，如果不存在则默认为0
            resp.setUnreadCount(unReadCountMap.getOrDefault(room.getRoomId(), 0));
            return resp;
        }).sorted(Comparator.comparing(ChatRoomResp::getActiveTime).reversed()) // 按照活跃时间降序排序
        .collect(Collectors.toList());
    }

    /**
     * 获取指定用户ID和房间ID列表的未读消息数量。
     *
     * @param uid 用户的唯一标识符，如果为null，则返回空的Map。
     * @param roomIds 房间ID的列表，用于查询这些房间对应的未读消息数。
     * @return 返回一个Map，其中键为房间ID，值为该房间对应的未读消息数量。
     */
    private Map<Long, Integer> getUnReadCountMap(Long uid, List<Long> roomIds) {
        // 当用户ID为null时，直接返回空的Map
        if (Objects.isNull(uid)) {
            return new HashMap<>();
        }
        // 根据房间ID列表和用户ID查询联系人信息
        List<Contact> contacts = contactDao.getByRoomIds(roomIds, uid);
        // 并行流处理联系人列表，映射每条联系记录到其对应的房间ID和未读消息数
        return contacts.parallelStream()
                .map(contact -> Pair.of(contact.getRoomId(), messageDao.getUnReadCount(contact.getRoomId(),contact.getReadTime())))
                // 收集映射结果到一个Map中
                .collect(Collectors.toMap(Pair::getKey,  Pair::getValue));
    }


    /**
     * 根据房间ID列表和用户ID，获取房间基础信息的映射表。
     *
     * @param roomIds 房间ID列表
     * @param uid 用户ID
     * @return 房间基础信息映射表，键为房间ID，值为房间的基础信息对象
     */
    private Map<Long, RoomBaseInfo> getRoomBaseInfoMap(List<Long> roomIds, Long uid) {
        // 从缓存中批量获取房间信息
        Map<Long, Room> roomMap = roomCache.getBatch(roomIds);

        // 根据房间类型将房间ID进行分组
        Map<Integer, List<Long>> groupRoomIdMap = roomMap.values().stream().collect(Collectors.groupingBy(Room::getType,
                Collectors.mapping(Room::getId, Collectors.toList())));

        // 批量获取群组房间的信息
        List<Long> groupRoomId = groupRoomIdMap.get(RoomTypeEnum.GROUP.getType());
        Map<Long, RoomGroup> roomInfoBatch = roomGroupCache.getBatch(groupRoomId);

        // 批量获取好友房间的信息
        List<Long> friendRoomId = groupRoomIdMap.get(RoomTypeEnum.FRIEND.getType());
        Map<Long, User> friendRoomMap = getFriendRoomMap(friendRoomId, uid);

        // 遍历所有房间，构造房间基础信息对象，并根据房间ID收集到映射表中
        return roomMap.values().stream().map(room -> {
            RoomBaseInfo roomBaseInfo = new RoomBaseInfo();
            // 设置房间基础信息
            roomBaseInfo.setRoomId(room.getId());
            roomBaseInfo.setType(room.getType());
            roomBaseInfo.setHotFlag(room.getHotFlag());
            roomBaseInfo.setLastMsgId(room.getLastMsgId());
            roomBaseInfo.setActiveTime(room.getActiveTime());
            // 根据房间类型设置房间的名称和头像
            if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.GROUP) {
                RoomGroup roomGroup = roomInfoBatch.get(room.getId());
                roomBaseInfo.setName(roomGroup.getName());
                roomBaseInfo.setAvatar(roomGroup.getAvatar());
            } else if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.FRIEND) {
                User user = friendRoomMap.get(room.getId());
                roomBaseInfo.setName(user.getName());
                roomBaseInfo.setAvatar(user.getAvatar());
            }
            return roomBaseInfo;
        }).collect(Collectors.toMap(RoomBaseInfo::getRoomId, Function.identity()));
    }

    /**
     * 根据房间ID列表和用户ID，获取朋友房间映射表。
     *
     * @param roomIds 房间ID列表，不可为空。
     * @param uid 当前用户ID。
     * @return 返回一个映射表，其中键为房间ID，值为对应房间内的用户信息。
     */
    private Map<Long, User> getFriendRoomMap(List<Long> roomIds, Long uid) {
        // 如果房间ID列表为空，则直接返回空映射表
        if (CollectionUtil.isEmpty(roomIds)) {
            return new HashMap<>();
        }
        // 根据房间ID列表，批量获取房间与朋友信息的映射表
        Map<Long, RoomFriend> roomFriendMap = roomFriendCache.getBatch(roomIds);
        // 根据房间与朋友信息的映射表，和当前用户ID，获取朋友的用户ID集合
        Set<Long> friendUidSet = ChatAdapter.getFriendUidSet(roomFriendMap.values(), uid);
        // 根据朋友的用户ID集合，批量获取用户信息
        Map<Long, User> userBatch = userInfoCache.getBatch(new ArrayList<>(friendUidSet));
        // 根据房间与朋友信息的映射表，和用户信息映射表，生成并返回最终的房间与用户信息映射表
        return roomFriendMap.values()
                .stream()
                .collect(Collectors.toMap(RoomFriend::getRoomId, roomFriend -> {
                    Long friendUid = ChatAdapter.getFriendUid(roomFriend, uid);
                    return userBatch.get(friendUid);
                }));
    }

    private Double getCursorOrNull(String cursor) {
        return Optional.ofNullable(cursor).map(Double::parseDouble).orElse(null);
    }
}
